From 18d12ffe75677cbc1d4d077be114c998bdf30dac Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Tue, 20 Nov 2007 10:26:57 +0000 Subject: [PATCH] add support for conditional icns gdk-pixbuf loader (Closes: #395738) 2007-11-20 Bastien Nocera * configure.in: add support for conditional icns gdk-pixbuf loader (Closes: #395738) 2007-11-20 Bastien Nocera * Makefile.am: * io-icns.c: Add icns (MacOS X icons) loader, based on work by Lyonel Vincent (Closes: #395738) svn path=/trunk/; revision=19007 --- ChangeLog | 5 + configure.in | 3 +- gdk-pixbuf/ChangeLog | 16 +- gdk-pixbuf/Makefile.am | 16 ++ gdk-pixbuf/io-icns.c | 396 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 430 insertions(+), 6 deletions(-) create mode 100644 gdk-pixbuf/io-icns.c diff --git a/ChangeLog b/ChangeLog index 6bdc73283f..1a248c4dda 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2007-11-20 Bastien Nocera + + * configure.in: add support for conditional icns gdk-pixbuf loader + (Closes: #395738) + 2007-11-19 10:31:26 Tim Janik * configure.in: updated version number to 2.15.0 for development. diff --git a/configure.in b/configure.in index 32d6143e05..22f5781431 100644 --- a/configure.in +++ b/configure.in @@ -886,7 +886,7 @@ else fi fi -all_loaders="png,bmp,wbmp,gif,ico,ani,jpeg,pnm,ras,tiff,xpm,xbm,tga,pcx" +all_loaders="png,bmp,wbmp,gif,ico,ani,jpeg,pnm,ras,tiff,xpm,xbm,tga,pcx,icns" included_loaders="" # If no loaders specified, include all if test "x$with_included_loaders" = xyes ; then @@ -930,6 +930,7 @@ AM_CONDITIONAL(INCLUDE_XPM, [test x"$INCLUDE_xpm" = xyes]) AM_CONDITIONAL(INCLUDE_XBM, [test x"$INCLUDE_xbm" = xyes]) AM_CONDITIONAL(INCLUDE_TGA, [test x"$INCLUDE_tga" = xyes]) AM_CONDITIONAL(INCLUDE_PCX, [test x"$INCLUDE_pcx" = xyes]) +AM_CONDITIONAL(INCLUDE_ICNS, [test x"$INCLUDE_icns" = xyes]) AC_HEADER_SYS_WAIT diff --git a/gdk-pixbuf/ChangeLog b/gdk-pixbuf/ChangeLog index c3cfcfddcf..dee96b8e76 100644 --- a/gdk-pixbuf/ChangeLog +++ b/gdk-pixbuf/ChangeLog @@ -1,3 +1,9 @@ +2007-11-20 Bastien Nocera + + * Makefile.am: + * io-icns.c: Add icns (MacOS X icons) loader, based on work by + Lyonel Vincent (Closes: #395738) + 2007-11-10 Matthias Clasen * io-jpeg.c: Avoid unaligned accesses that cause @@ -147,7 +153,7 @@ Sat Jul 7 17:34:33 2007 Tim Janik * gdk-pixbuf-simple-anim.c (gdk_pixbuf_simple_anim_add_frame): Don't double the delay time of the first frame. (#431997, - Björn Lindqvist) + Bj?rn Lindqvist) 2007-04-25 Matthias Clasen @@ -1583,7 +1589,7 @@ Mon Aug 18 10:25:29 2003 Owen Taylor * pixops/pixops.c (correct_total): Split correction into multiple pieces if no single weight is large enough to apply the unsplit - correction. (#117431, problem reported by Tomas Ögren) + correction. (#117431, problem reported by Tomas ?gren) 2003-07-13 Matthias Clasen @@ -2371,7 +2377,7 @@ Wed Feb 27 18:33:04 2002 Owen Taylor * gdk-pixdata.c (gdk_pixdata_to_csource): Use {} not () to group around string assigned to char[]. (#72767, - Tomas Ögren) + Tomas ?gren) 2002-02-21 Havoc Pennington @@ -3594,7 +3600,7 @@ Wed Jun 21 16:02:48 2000 Owen Taylor 2000-06-05 Mathieu Lacage * configure.in: add some gtk parameters to the - GDK_PIXBUF_LIBÃ�²S and GDK_PIXBUG_INCLUDEDIR vars. One more + GDK_PIXBUF_LIB?�?S and GDK_PIXBUG_INCLUDEDIR vars. One more fight in my crusade for strange prefix compile... 2000-05-30 Not Zed @@ -3691,7 +3697,7 @@ Fri May 5 12:16:32 2000 Owen Taylor * gdk-pixbuf/Makefile.am (INCLUDES): Add $(GNOME_CFLAGS). Reported by Jens Finke. -2000-04-14 Tomasz KÃ�³opczko +2000-04-14 Tomasz K?�?opczko * gdk-pixbuf/pixops/makefile.am: $(LIBART_CFLAGS) replaced by $(GTK_CFLAGS) - now gdk-pixbuf compiles correctly. diff --git a/gdk-pixbuf/Makefile.am b/gdk-pixbuf/Makefile.am index 683f3c9de2..4fb5e0b07f 100644 --- a/gdk-pixbuf/Makefile.am +++ b/gdk-pixbuf/Makefile.am @@ -164,6 +164,14 @@ libpixbufloader_tga_la_SOURCES = io-tga.c libpixbufloader_tga_la_LDFLAGS = -avoid-version -module $(no_undefined) libpixbufloader_tga_la_LIBADD = $(module_libs) +# +# The .icns loader +# +libpixbufloader_static_icns_la_SOURCES = io-icns.c +libpixbufloader_icns_la_SOURCES = io-icns.c +libpixbufloader_icns_la_LDFLAGS = -avoid-version -module $(no_undefined) +libpixbufloader_icns_la_LIBADD = $(module_libs) + # # The PCX loader # @@ -262,6 +270,12 @@ else PCX_LIB = libpixbufloader-pcx.la endif +if INCLUDE_ICNS +STATIC_ICNS_LIB = libpixbufloader-static-icns.la +else +ICNS_LIB = libpixbufloader-icns.la +endif + if BUILD_DYNAMIC_MODULES loader_LTLIBRARIES = \ @@ -278,6 +292,7 @@ loader_LTLIBRARIES = \ $(XPM_LIB) \ $(XBM_LIB) \ $(TGA_LIB) \ + $(ICNS_LIB) \ $(PCX_LIB) @@ -297,6 +312,7 @@ noinst_LTLIBRARIES = \ $(STATIC_XPM_LIB) \ $(STATIC_XBM_LIB) \ $(STATIC_TGA_LIB) \ + $(STATIC_ICNS_LIB) \ $(STATIC_PCX_LIB) builtin_objs = @INCLUDED_LOADER_OBJ@ diff --git a/gdk-pixbuf/io-icns.c b/gdk-pixbuf/io-icns.c new file mode 100644 index 0000000000..98ad97dd50 --- /dev/null +++ b/gdk-pixbuf/io-icns.c @@ -0,0 +1,396 @@ +/* Mac OS X .icns icons loader + * + * Copyright (c) 2007 Lyonel Vincent + * Copyright (c) 2007 Bastien Nocera + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#define _GNU_SOURCE +#include +#include +#include + +#include "gdk-pixbuf-private.h" +#include "gdk-pixbuf-io.h" + +G_MODULE_EXPORT void fill_vtable (GdkPixbufModule * module); +G_MODULE_EXPORT void fill_info (GdkPixbufFormat * info); + +#define IN /**/ +#define OUT /**/ +#define INOUT /**/ + +struct IcnsBlockHeader +{ + char id[4]; + guint32 size; /* caution: bigendian */ +}; +typedef struct IcnsBlockHeader IcnsBlockHeader; + +/* + * load raw icon data from 'icns' resource + * + * returns TRUE when successful + */ +static gboolean +load_resources (unsigned size, IN gpointer data, gsize datalen, + OUT guchar ** picture, OUT gsize * plen, + OUT guchar ** mask, OUT gsize * mlen) +{ + IcnsBlockHeader *header = NULL; + const char *bytes = NULL; + const char *current = NULL; + guint32 blocklen = 0; + guint32 icnslen = 0; + gboolean needs_mask = TRUE; + + if (datalen < 2 * sizeof (guint32)) + return FALSE; + if (!data) + return FALSE; + + *picture = *mask = NULL; + *plen = *mlen = 0; + + bytes = data; + header = (IcnsBlockHeader *) data; + if (memcmp (header->id, "icns", 4) != 0) + return FALSE; + + icnslen = GUINT32_FROM_BE (header->size); + if ((icnslen > datalen) || (icnslen < 2 * sizeof (guint32))) + return FALSE; + + current = bytes + sizeof (IcnsBlockHeader); + while ((current - bytes < icnslen) && (icnslen - (current - bytes) >= sizeof (IcnsBlockHeader))) + { + header = (IcnsBlockHeader *) current; + blocklen = GUINT32_FROM_BE (header->size); + + /* Check that blocklen isn't garbage */ + if (blocklen > icnslen - (current - bytes)) + return FALSE; + + switch (size) + { + case 256: + if (memcmp (header->id, "ic08", 4) == 0) /* 256x256 icon */ + { + *picture = (gpointer) (current + sizeof (IcnsBlockHeader)); + *plen = blocklen - sizeof (IcnsBlockHeader); + } + needs_mask = FALSE; + break; + case 128: + if (memcmp (header->id, "it32", 4) == 0) /* 128x128 icon */ + { + *picture = (gpointer) (current + sizeof (IcnsBlockHeader)); + *plen = blocklen - sizeof (IcnsBlockHeader); + if (memcmp (*picture, "\0\0\0\0", 4) == 0) + { + *picture += 4; + *plen -= 4; + } + } + if (memcmp (header->id, "t8mk", 4) == 0) /* 128x128 mask */ + { + *mask = (gpointer) (current + sizeof (IcnsBlockHeader)); + *mlen = blocklen - sizeof (IcnsBlockHeader); + } + break; + case 48: + if (memcmp (header->id, "ih32", 4) == 0) /* 48x48 icon */ + { + *picture = (gpointer) (current + sizeof (IcnsBlockHeader)); + *plen = blocklen - sizeof (IcnsBlockHeader); + } + if (memcmp (header->id, "h8mk", 4) == 0) /* 48x48 mask */ + { + *mask = (gpointer) (current + sizeof (IcnsBlockHeader)); + *mlen = blocklen - sizeof (IcnsBlockHeader); + } + break; + case 32: + if (memcmp (header->id, "il32", 4) == 0) /* 32x32 icon */ + { + *picture = (gpointer) (current + sizeof (IcnsBlockHeader)); + *plen = blocklen - sizeof (IcnsBlockHeader); + } + if (memcmp (header->id, "l8mk", 4) == 0) /* 32x32 mask */ + { + *mask = (gpointer) (current + sizeof (IcnsBlockHeader)); + *mlen = blocklen - sizeof (IcnsBlockHeader); + } + break; + case 16: + if (memcmp (header->id, "is32", 4) == 0) /* 16x16 icon */ + { + *picture = (gpointer) (current + sizeof (IcnsBlockHeader)); + *plen = blocklen - sizeof (IcnsBlockHeader); + } + if (memcmp (header->id, "s8mk", 4) == 0) /* 16x16 mask */ + { + *mask = (gpointer) (current + sizeof (IcnsBlockHeader)); + *mlen = blocklen - sizeof (IcnsBlockHeader); + } + break; + default: + return FALSE; + } + + current += blocklen; + } + + if (!*picture) + return FALSE; + if (needs_mask && !*mask) + return FALSE; + return TRUE; +} + +/* + * uncompress RLE-encoded bytes into RGBA scratch zone: + * if firstbyte >= 0x80, it indicates the number of identical bytes + 125 + * (repeated value is stored next: 1 byte) + * otherwise, it indicates the number of non-repeating bytes - 1 + * (non-repeating values are stored next: n bytes) + */ +static gboolean +uncompress (unsigned size, INOUT guchar ** source, OUT guchar * target, INOUT gsize * _remaining) +{ + guchar *data = *source; + gsize remaining; + gsize i = 0; + + /* The first time we're called, set remaining */ + if (*_remaining == 0) { + remaining = size * size; + } else { + remaining = *_remaining; + } + + while (remaining > 0) + { + guint8 count = 0; + + if (data[0] & 0x80) /* repeating byte */ + { + count = data[0] - 125; + + if (count > remaining) + return FALSE; + + for (i = 0; i < count; i++) + { + *target = data[1]; + target += 4; + } + + data += 2; + } + else /* non-repeating bytes */ + { + count = data[0] + 1; + + if (count > remaining) + return FALSE; + + for (i = 0; i < count; i++) + { + *target = data[i + 1]; + target += 4; + } + data += count + 1; + } + + remaining -= count; + } + + *source = data; + *_remaining = remaining; + return TRUE; +} + +static GdkPixbuf * +load_icon (unsigned size, IN gpointer data, gsize datalen) +{ + guchar *icon = NULL; + guchar *mask = NULL; + gsize isize = 0, msize = 0, i; + guchar *image = NULL; + + if (!load_resources (size, data, datalen, &icon, &isize, &mask, &msize)) + return NULL; + + /* 256x256 icons don't use RLE or uncompressed data, + * They're usually JPEG 2000 images */ + if (size == 256) + { + GdkPixbufLoader *loader; + GdkPixbuf *pixbuf; + + loader = gdk_pixbuf_loader_new (); + if (!gdk_pixbuf_loader_write (loader, icon, isize, NULL) + || !gdk_pixbuf_loader_close (loader, NULL)) + { + g_object_unref (loader); + return NULL; + } + + pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); + g_object_ref (pixbuf); + g_object_unref (loader); + + return pixbuf; + } + + g_assert (mask); + + if (msize != size * size) /* wrong mask size */ + return NULL; + + image = (guchar *) g_try_malloc0 (size * size * 4); /* 4 bytes/pixel = RGBA */ + + if (!image) + return NULL; + + if (isize == size * size * 4) /* icon data is uncompressed */ + for (i = 0; i < size * size; i++) /* 4 bytes/pixel = ARGB (A: ignored) */ + { + image[i * 4] = icon[4 * i + 1]; /* R */ + image[i * 4 + 1] = icon[4 * i + 2]; /* G */ + image[i * 4 + 2] = icon[4 * i + 3]; /* B */ + } + else + { + guchar *data = icon; + gsize remaining = 0; + + /* R */ + if (!uncompress (size, &data, image, &remaining)) + goto bail; + /* G */ + if (!uncompress (size, &data, image + 1, &remaining)) + goto bail; + /* B */ + if (!uncompress (size, &data, image + 2, &remaining)) + goto bail; + } + + for (i = 0; i < size * size; i++) /* copy mask to alpha channel */ + image[i * 4 + 3] = mask[i]; + + return gdk_pixbuf_new_from_data ((guchar *) image, GDK_COLORSPACE_RGB, /* RGB image */ + TRUE, /* with alpha channel */ + 8, /* 8 bits per sample */ + size, /* width */ + size, /* height */ + size * 4, /* no gap between rows */ + (GdkPixbufDestroyNotify)g_free, /* free() function */ + NULL); /* param to free() function */ + +bail: + g_free (image); + return NULL; +} + +static int sizes[] = { + 256, /* late-Tiger icons */ + 128, /* Standard OS X */ + 48, /* Not very common */ + 32, /* Standard Mac OS Classic (8 & 9) */ + 24, /* OS X toolbars */ + 16 /* used in Mac OS Classic and dialog boxes */ +}; + +static GdkPixbuf * +icns_image_load (FILE *f, GError ** error) +{ + GByteArray *data; + GdkPixbuf *pixbuf = NULL; + guint i; + + data = g_byte_array_new (); + while (!feof (f)) + { + gint save_errno; + guchar buf[4096]; + gsize bytes; + + bytes = fread (buf, 1, sizeof (buf), f); + save_errno = errno; + data = g_byte_array_append (data, buf, bytes); + + if (ferror (f)) + { + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (save_errno), + _("Error reading ICNS image: %s"), + g_strerror (save_errno)); + + g_byte_array_free (data, TRUE); + + return NULL; + } + } + + for (i = 0; i < G_N_ELEMENTS(sizes) && !pixbuf; i++) + pixbuf = load_icon (sizes[i], data->data, data->len); + + g_byte_array_free (data, TRUE); + + if (!pixbuf) + g_set_error (error, GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_CORRUPT_IMAGE, + _("Could not decode ICNS file")); + + return pixbuf; +} + +void +fill_vtable (GdkPixbufModule * module) +{ + module->load = icns_image_load; +} + +void +fill_info (GdkPixbufFormat * info) +{ + static GdkPixbufModulePattern signature[] = { + {"icns", NULL, 100}, /* file begins with 'icns' */ + {NULL, NULL, 0} + }; + static gchar *mime_types[] = { + "image/x-icns", + NULL + }; + static gchar *extensions[] = { + "icns", + NULL + }; + + info->name = "icns"; + info->signature = signature; + info->description = N_("The ICNS image format"); + info->mime_types = mime_types; + info->extensions = extensions; + info->flags = GDK_PIXBUF_FORMAT_THREADSAFE; + info->license = "GPL"; + info->disabled = FALSE; +} + -- 2.30.2